\section{Q\&A}
\subsection{内存区域}
一个由C/C++编译的程序占用的内存分为以下几个部分：
\begin{enumerate}
    \item 栈区（stack）— 由编译器自动分配释放 ，存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。其操作方式类似于数据结构中的栈。
    \item 堆区（heap） — 一般由程序员分配释放， 若程序员不释放，程序结束 时可能由OS回收 。分配方式类似于链表。
    \item 全局区（静态区）（static）—存放全局变量、静态数据、常量。程序结束后由系统释放。
    \item 文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放。
    \item 程序代码区—存放函数体（类成员函数和全局函数）的二进制代码。
\end{enumerate}
\subsection{new和malloc}
动态内存分配

在执行程序的过程中动态的分配或者回收存储空间的分配内存的方法。

例如：当我们在定义一个数组时，必须要用一个常量来确定在编译时分配多大的空间。但是在真正使用数组时，我们才会知道我们需要多少内存空间，所以我们要根据程序的需要即时分配，这就牵扯到内存的动态开辟。

C语言中
malloc、calloc、realloc、free
\begin{itemize}
    \item malloc：void *malloc(unsigned int size) 开辟一块长度为size的连续内存空间，返回类型为void类型的指针。在使用malloc开辟一段空间时，void*要显示的转换为所需要的类型，如果开辟失败，则返回NULL指针。
    \item \cppinline{calloc void* calloc (size_t num, size_t size);} 开辟一块num个大小为size的连续空间，并将每一块空间初始化为0。
    \item \cppinline{realloc void *realloc(void *ptr,size_t size);} 将内存地址为ptr的一段空间的大小调整为size大小。如果ptr这段空间后面有足够的空间，就直接追加上来 如果ptr后面的空间不足，则在堆上重新开辟一块合适大小的连续空间，将原有数据拷贝到新的内存空间中，释放掉原来的内存，最后返回的则是新地址。
    \item \cppinline{free void free (void* ptr);} free函数是来释放动态开辟的内存的。
\end{itemize}
malloc和free要配套使用，如果没有free则会造成内存泄漏。
malloc()从哪里获取的内存？
动态开辟的从堆里获取空间。就是说malloc函数返回的指针是指向堆里的一块内存。操作系统中有一个记录空闲内存地址的链表，当操作系统收到程序的申请时，就会遍历链表

C++中的new、delete
\begin{itemize}
    \item new
          new运算返回所分配内存单元的起始地址，所以需要把返回值保存在一个指针变量中。若分配不成功，返回NULL，并抛出异常。
          new没有为创建的对象命名，只能通过指针去访问对象或者数组。
    \item delete
          delete <指针变量>
          delete []<动态分配的数组名>
\end{itemize}
new和delete必须配对使用。
虽然程序结束后系统会自动释放程序和其中数据所占的内存空间，但是为了在程序运行过程中能够重复使用有限的内存资源，防止系统产生内存泄漏，还是应该即时释放不需要的动态分配的内存单元，以便系统能随时对该内存单元进行分配。
delete释放内存，只是销毁内存上的对象，但是指针仍然存在，仍然指向原来的内存，保存原来空间的地址。所以我们应该在释放之后将指针置空，以避免后面不小心解引用造成问题。

例：
\begin{cpp}
    int *p1 = new int;
    float *p2 = new float;

    char *pBuffer = new char[100];
    int *pA = new int[size];
    student *stu = new student[num];

    int *pNum = new int(100);


    delete pNum;
    delete []pBuffer;
\end{cpp}
malloc 和 new的区别？

malloc/free是标准库函数，new/delete是C++运算符
malloc失败返回空，new失败抛异常
new/delete会调用构造、析构函数，malloc/free不会，所以他们无法满足动态对象的要求。
new返回有类型的指针，malloc返回无类型的指针
还有没有其他的理解？
\begin{enumerate}
    \item 分配内存的位置
          malloc是从堆上动态分配内存，new是从自由存储区为对象动态分配内存。
          自由存储区的位置取决于operator new的实现。自由存储区不仅可以为堆，还可以是静态存储区，这都看operator new在哪里为对象分配内存。

    \item 返回类型安全性
          malloc内存分配成功后返回void*，然后再强制类型转换为需要的类型；new操作符分配内存成功后返回与对象类型相匹配的指针类型；因此new是符合类型安全的操作符。

    \item 内存分配失败返回值
          malloc内存分配失败后返回NULL；new分配内存失败则会抛异常（bac\_alloc）。
          \begin{cpp}
              try
              {
              int *a = new int();
              }
              catch (bad_alloc)
              {
                      ...
                  }
          \end{cpp}
    \item 分配内存的大小的计算
          使用new操作符申请内存分配时无须指定内存块的大小，编译器会根据类型信息自行计算，而malloc则需要显式地指出所需内存的尺寸。

    \item 是否调用构造函数/析构函数
          使用new操作符来分配对象内存时会经历三个步骤：
          - 第一步：调用operator new 函数（对于数组是operator new[]）分配一块足够大的，原始的，未命名的内存空间以便存储特定类型的对象。
          - 第二步：编译器运行相应的构造函数以构造对象，并为其传入初值。
          - 第三步：对象构造完成后，返回一个指向该对象的指针。

          使用delete操作符来释放对象内存时会经历两个步骤：
          - 第一步：调用对象的析构函数。
          - 第二步：编译器调用operator delete(或operator delete[])函数释放内存空间。

          总之来说，new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构；而malloc则不会。
\end{enumerate}
6.对数组的处理
C++提供了new []和delete []用来专门处理数组类型。它会调用构造函数初始化每一个数组元素，然后释放对象时它会为每个对象调用析构函数，但是二者一定要配套使用；至于malloc，它并不知道你要在这块空间放置数组还是其他的东西，就只给一块原始的空间，再给一个内存地址就完事，如果要动态开辟一个数组的内存，还需要我们手动自定数组的大小。
\begin{cpp}
    A * ptr = new A[10];//分配10个A对象
    delete [] ptr;
    int * ptr = (int *) malloc( sizeof(int) * 10);//分配一个10个int元素的数组
\end{cpp}
7.new与malloc是否可以相互调用
operator new /operator delete的实现可以基于malloc，而malloc的实现不可以去调用new

8.是否可以被重载
opeartor new /operator delete可以被重载。而malloc/free则不能重载。

9.分配内存时内存不足
malloc动态分配内存后，如果不够用可以使用realloc函数重新分配实现内存的扩充；而new则没有这样的操作；

总结
将上面所述的10点差别整理成表格：
\begin{table}[!htbp]
    \begin{tabular}{ccc}
        特征               & new/delete                            & malloc/free                          \\
        分配内存的位置     & 自由存储区                            & 堆                                   \\
        返回类型安全性     & 完整类型指针                          & void*                                \\
        内存分配失败返回值 & 默认抛出异常                          & 返回NULL                             \\
        分配内存的大小     & 由编译器根据类型计算得出              & 必须显式指定字节数                   \\
        处理数组           & 有处理数组的new版本new[]              & 需要用户计算数组的大小后进行内存分配 \\
        已分配内存的扩充   & 无法直观地处理                        & 使用realloc简单完成                  \\
        是否相互调用       & 可以，看具体的operator new/delete实现 & 不可调用new                          \\
        分配内存时内存不足 & 无法通过用户代码进行处理              & 能够使用realloc函数或重新制定分配器  \\
        函数重载           & 允许                                  & 不允许                               \\
        构造函数与析构函数 & 调用                                  & 不调用                               \\
    \end{tabular}
\end{table}
delete与delete []

delete是回收new开辟出的单个对象指针指向的内存。
delete[]是回收new [] 开辟出的对象数组指针指向的内存。
new[]开辟数组空间要多出4个字节来存放数组大小。
delete []要与new []要配套使用

有了malloc/free为什么还要new/delete?

new运算不需要进行强制类型转换，使用简单方便；
new运算是通过调用构造函数初始化动态创建的对象，执行效率更高；
使用new能够进行异常处理，使用更安全
需要注意的问题：

如果你在函数上面定义了一个指针变量，然后在这个函数里申请了一块动态分配的内存让指针指向它。实际上，这个指针的地址是在栈上，但是它所指向的内容却是在堆上面。这一点要注意！所以，在一个函数里申请了空间后，比如：
\begin{cpp}
    void Function(void)
    {
            char *p = (char *)malloc(100 * sizeof(char));
        }
\end{cpp}
千万不要认为函数返回，函数所在的栈被销毁指针也跟着销毁，申请的内存也就一样跟着销毁了。这是错误的。因为申请的内存在堆上，除非程序员手动释放，否则要等到程序结束释放所有内存才会释放它们，跟函数是否结束没有关系。也就是函数所在的栈被销毁跟堆完全没有关系。所以，忠告就是：使用完不再需要记得释放动态分配的内存！在该部分程序退出之前释放内存并立即给P赋0值(NULL)。另一个办法是保证P在没有初始化之前，将不再被使用。
\subsection{如何在堆上创建对象？}
想法一：如果只能在堆上创建对象的话也就意味着不能在栈上创建对象，而我们知道要创建对象必须调用类的构造函数，在这里有很多人就会想到将构造函数设置为私有的，这样一来就只能通过new来创建对象，可是一旦将构造函数设置为私有的new也无法对对象进行初始化了。
new为创建对象需要两步，即先调用库函数operator new开辟空间，后调用类的构造函数对空间进行初始化。此时如果将构造函数设置为私有的，new就无法初始化对象了，只能为对象分配空间。

想法二：其实在创建对象的时候，编译器会先检查类的析构函数能否调用，因为编译器要保证开辟出去的空间要能回收回来呀，所以此时如果将类的析构函数设置为私有的，就编译器就不会调用类的构造函数在栈上创建对象了。但是问题又来了，一般析构函数是要在对象的生命周期完结之后进行一些清理工作的，即如果使用new运算符在堆上开辟空间了，析构函数中必然要用delete对空间进行释放，如果将析构函数设置为私有的，就不能将堆上申请的空间释放掉了，此时可以将delete定义在类的一个公有成员函数中，在类外显示地调用一下这个成员函数就可以将堆上申请的空间释放掉了。
\begin{remark}
    必须要在类内的成员函数内使用delete来释放空间，因为如果在类外使用delete来释放空间的话，delete就会调用类的析构函数，此时析构函数是私有的，不能进行调用，而在类内疚可以进行调用了。
\end{remark}
\subsection{继承关系}
三种访问权限：
\begin{itemize}
    \item public:可以被任意实体访问（修饰的变量或者方法）。
    \item protected:只允许子类及本类的成员函数访问（修饰的变量或者方法）。
    \item private:只允许本类的成员函数访问（修饰的变量或者方法）。
\end{itemize}
\begin{table}[!htbp]
    \caption{继承和访问权限}
    \centering
    \begin{tabular}{ccc}
        \toprule
        成员和变量的属性&继承方式&继承后变量或方法的属性\\ 
        \midrule
        public & public继承 & public           \\
        protected & public继承 & protected     \\
        private & public继承 & 子类无权访问    \\
        public & protected继承 & protected     \\
        protected & protected继承 & protected  \\
        private & protected继承 & 子类无权访问 \\
        public & private继承 & private        \\
        protected & private继承 & private     \\
        private & private继承 & 子类无权访问  \\
        \bottomrule
    \end{tabular}
\end{table}
\begin{itemize}
    \item 类中的访问权限修饰private、protected、public修饰的域对类内部无限制限制对象对齐的直接访问。
    \item 访问权限修饰的继承影响子类对父类的访问权限子类：
        \begin{itemize}
            \item public继承方式：继承后父类的访问修饰符和子类一样。
            \item protected继承方式：继承后public提升为protected。
            \item private继承方式：继承后所有权限均提升为private。
        \end{itemize}
\end{itemize}
\subsection{线程通信}
\begin{enumerate}
    \item 互斥锁
    \item 读写锁
    \item 信号量
    \item 条件变量
\end{enumerate}
\subsection{进程的组成}
\begin{itemize}
    \item 进程控制块(PCB)是用来记录进程状态及其他相关信息的数据结构,PCB是进程存在的唯一标志，PCB存在则进程存在。系统创建进程时会产生一个PCB，撤销进程时，PCB也自动消失。
    \item UNIX系统为了节省进程控制块所占的内存空间，把每个进程控制块分成两部分。一部分常驻内存，不管进程是否正占有处理器运行，系统经常会对这部分内容进行查询和处理，常驻部分内容包括：进程状态、优先数、过程特征、数据段始址、等待原因和队列指针等，这是进行处理器调度时必须使用的一些主要信息。另一部分非常驻内存，当进程不占有处理器时，系统不会对这部分内容进行查询和处理，因此这部分内容可以存放在磁盘的对换区中，它随用户的程序和数据部分换进或换出内存。
    \item UNIX系统把进程的数据段又划分成三部分:用户栈区(供用户程序使用的信息区)；用户数据区(包括用户工作数据和非可重入的程序段)；系统数据区(包括系统变量和对换信息)。
    \item 正文段是可重入的程序，能被若干进程共享。为了管理可共享的正文段，UNIX设置了一张正文表，每个正文段都占用一个表目，用来指出该正文段在内存和磁盘上的位置、段的大小以及调用该段的进程数等情况。
\end{itemize}
\subsection{数组和指针的区别}
当我们声明一个数组时，其数组的名字也是一个指针，该指针指向数组的第一个元素。我们可以使用指针访问数组。但是C++没有记录数组的大小，因此在用指针访问数组中的元素时，需要确保没有超出数组的边界。当数组作为参数传递给函数的时候退化为指针。
\cppfile{code/question_code/qa1.cc}
\subsection{函数指针}
\begin{itemize}
    \item 获取函数的地址
    \item 声明函数指针
    \item 使用指针调用函数
\end{itemize}
\cppfile{code/QA/funptr.cc}
函数指针表示:
\begin{itemize}
    \item \cppinline{const double * f1(const double ar[],int n)}
    \item \cppinline{const double * f1(const double [],int n)}
    \item \cppinline{const double * f1(const double *,int n)}
\end{itemize}
这三个函数一样，现在声明函数指针，\cppinline{const double * (*p1)(const double *,int)}。声明的同时初始化:
\cppinline{const double *(*p1)(const double *,int)=f1}或者自动推断:\\*
\cppinline{auto p2=f2 //auto自动推断变量p2的类型初始化为f2}\\*
声明包含三个函数指针的数组:\\*
\cppinline{const double *(*pa[3])(const double *,int)={f1,f2,f3}}，为什么这么声明？首先使用pa[3].运算符[]的优先级高于*,因此*pa[3]表示pa是一个包含三个指针的数组。上述声明的其他部分指出了每个指针指向的是什么特征标。调用如下:\\*
\cppinline{const double * px=pa[0](av,3);}\\*
或者\\*
\cppinline{const double * py=(*pb[1](av,3));}\\*
获得指向double的值，可以使用运算符*：
\cppinline{double x = *pa[0](av,3);}\\*
\cppinline{double x = *(*pa[1])(av,3);}\\*
函数指针和函数指针数组相关使用:
\cppfile{code/QA/funptr2.cc}
C++还能使用Typedef简化声明:
\begin{cpp}
    typedef const double *(*p_func)(const double *,int)
    p_fun p1 = f1;
    //使用
    p_fun pa[3] = {f1,f2,f3};
    p_fun (*pd)[3] =&pa
\end{cpp}
\subsection{malloc和new的差别}
\begin{figure}[!htbp]
    \centering
    \includegraphics[width=\textwidth]{program_mem.pdf}
    \caption{内存区域}
    \label{fig:prgram_men}
\end{figure}

一个由C/C++编译的程序占用的内存分为以下几个部分：
\begin{enumerate}
    \item 栈区（stack)：编译器自动分配释放 ，存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。其操作方式类似于数据结构中的栈。
    \item 堆区（heap）：一般由程序员分配释放， 若程序员不释放，程序结束时由操作系统回收。分配方式类似于链表。
    \item 全局区（静态区）（static）：存放全局变量、静态数据、常量。程序结束后由系统释放。
    \item 文字常量区 ：常量字符串就是放在这里的。 程序结束后由系统释放。
    \item 程序代码区：存放函数体（类成员函数和全局函数）的二进制代码。
\end{enumerate}
动态内存分配：执行程序的过程中动态的分配或者回收存储空间的分配内存的方法。

例如：当我们在定义一个数组时，必须要用一个常量来确定在编译时分配多大的空间。但是在真正使用数组时，我们才会知道我们需要多少内存空间，所以我们要根据程序的需要及时分配，这就牵扯到内存的动态开辟。

C语言中的malloc、calloc、realloc、free
\begin{itemize}
    \item malloc：\cppinline{void *malloc(unsigned int size)} 开辟一块长度为size的连续内存空间，返回类型为void类型的指针。在使用malloc开辟一段空间时，void*要显示的转换为所需要的类型，如果开辟失败，则返回NULL指针。
    \item calloc \cppinline{void* calloc (size_t num, size_t size);} 开辟一块num个大小为size的连续空间，并将每一块空间初始化为0。
    \item realloc \cppinline{void *realloc(void *ptr,size_t size);} 将内存地址为ptr的一段空间的大小调整为size大小。如果ptr这段空间后面有足够的空间，就直接追加上来 如果ptr后面的空间不足，则在堆上重新开辟一块合适大小的连续空间，将原有数据拷贝到新的内存空间中，释放掉原来的内存，最后返回的则是新地址。
    \item free \cppinline{void free (void* ptr);} free函数是来释放动态开辟的内存的。
\end{itemize}
malloc和free要配套使用，如果没有free则会造成内存泄漏。

malloc()从哪里获取的内存？

动态开辟的从堆里获取空间。就是说malloc函数返回的指针是指向堆里的一块内存。操作系统中有一个记录空闲内存地址的链表，当操作系统收到程序的申请时，就会遍历链表。

C++中的new、delete
\begin{itemize}
    \item new运算返回所分配内存单元的起始地址，所以需要把返回值保存在一个指针变量中。若分配不成功，返回NULL，并抛出异常。 new没有为创建的对象命名，只能通过指针去访问对象或者数组。

    \item
          \begin{itemize}
              \item delete <指针变量>
              \item delete []<动态分配的数组名>
          \end{itemize}
\end{itemize}
\begin{remark}
    new和delete必须配对使用。
\end{remark}

虽然程序结束后系统会自动释放程序和其中数据所占的内存空间，但是为了在程序运行过程中能够重复使用有限的内存资源，防止系统产生内存泄漏，还是应该及时释放不需要的动态分配的内存单元，以便系统能随时对该内存单元进行分配。
delete释放内存，只是销毁内存上的对象，但是指针仍然存在，仍然指向原来的内存，保存原来空间的地址。所以我们应该在释放之后将指针置空，以避免后面不小心解引用造成问题。

\begin{cpp}
    int *p1 = new int;
    float *p2 = new float;
    char *pBuffer = new char[100];
    int *pA = new int[size];
    student *stu = new student[num];
    int *pNum = new int(100);
    delete pNum;
    delete []pBuffer;
\end{cpp}
\begin{enumerate}
    \item malloc/free是标准库函数，new/delete是C++运算符
    \item malloc失败返回空，new失败抛异常
    \item new/delete会调用构造、析构函数，malloc/free不会，所以他们无法满足动态对象的要求。
    \item new返回有类型的指针，malloc返回无类型的指针
\end{enumerate}
\begin{itemize}
    \item 分配内存的位置：malloc是从堆上动态分配内存，new是从自由存储区为对象动态分配内存。
          自由存储区的位置取决于operator new的实现。自由存储区不仅可以为堆，还可以是静态存储区，这都看operator new在哪里为对象分配内存。
    \item 返回类型安全性：malloc内存分配成功后返回void*，然后再强制类型转换为需要的类型；new操作符分配内存成功后返回与对象类型相匹配的指针类型；因此new是符合类型安全的操作符。
    \item 内存分配失败返回值：malloc内存分配失败后返回NULL；new分配内存失败则会抛异常（bad\_alloc）。
          \begin{cpp}
              try
              {
              int *a = new int();
              }
              catch (bad_alloc)
              {
                      ...
                  }
          \end{cpp}
    \item 分配内存的大小的计算：使用new操作符申请内存分配时无须指定内存块的大小，编译器会根据类型信息自行计算，而malloc则需要显式地指出所需内存的尺寸。

    \item 是否调用构造函数/析构函数：
          使用new操作符来分配对象内存时会经历三个步骤：
          \begin{enumerate}
              \item[第一步]调用operator new 函数（对于数组是operator new[ ]）分配一块足够大的，原始的，未命名的内存空间以便存储特定类型的对象。
              \item[第二步]编译器运行相应的构造函数以构造对象，并为其传入初值。
              \item[第三步]对象构造完成后，返回一个指向该对象的指针。
          \end{enumerate}
          使用delete操作符来释放对象内存时会经历两个步骤：
          \begin{itemize}
              \item[第一步]：调用对象的析构函数。
              \item[第二步]：编译器调用operator delete(或operator delete[ ])函数释放内存空间。
          \end{itemize}
          总之来说，new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构；而malloc则不会。
\end{itemize}
对数组的处理
C++提供了new [ ]和delete [ ]用来专门处理数组类型。它会调用构造函数初始化每一个数组元素，然后释放对象时它会为每个对象调用析构函数，但是二者一定要配套使用；至于malloc，它并不知道你要在这块空间放置数组还是其他的东西，就只给一块原始的空间，再给一个内存地址就完事，如果要动态开辟一个数组的内存，还需要我们手动自定数组的大小。
\begin{cpp}
    A * ptr = new A[10];//分配10个A对象
    delete [ ] ptr;
    int * ptr = (int *) malloc( sizeof(int) * 10);//分配一个10个int元素的数组
\end{cpp}
new与malloc是否可以相互调用？

operator new /operator delete的实现可以基于malloc，而malloc的实现不可以去调用new。\\*
是否可以被重载？

opeartor new /operator delete可以被重载。而malloc/free则不能重载。\\*
分配内存时内存不足？

malloc动态分配内存后，如果不够用可以使用realloc函数重新分配实现内存的扩充；而new则没有这样的操作；
\begin{table}[!htbp]
    \centering
    \caption{区别}
    \begin{tabular}{p{0.2\textwidth}p{0.4\textwidth}p{0.4\textwidth}}
        \toprule
        特征               & new/delete                            & malloc/free                          \\
        \midrule
        分配内存的位置     & 自由存储区                            & 堆                                   \\
        返回类型安全性     & 完整类型指针                          & void*                                \\
        内存分配失败返回值 & 默认抛出异常                          & 返回NULL                             \\
        分配内存的大小     & 由编译器根据类型计算得出              & 必须显式指定字节数                   \\
        处理数组           & 有处理数组的new版本new[ ]             & 需要用户计算数组的大小后进行内存分配 \\
        已分配内存的扩充   & 无法直观地处理                        & 使用realloc简单完成                  \\
        是否相互调用       & 可以，看具体的operator new/delete实现 & 不可调用new                          \\
        分配内存时内存不足 & 无法通过用户代码进行处理              & 能够使用realloc函数或重新指定分配器  \\
        函数重载           & 允许                                  & 不允许                               \\
        构造函数与析构函数 & 调用                                  & 不调用                               \\
        \bottomrule
    \end{tabular}
\end{table}
\newline
\textbf{delete与delete [ ]}
\begin{itemize}
    \item delete是回收new开辟出的单个对象指针指向的内存。
    \item delete[ ]是回收new [ ] 开辟出的对象数组指针指向的内存。
    \item new[ ]开辟数组空间要多出4个字节来存放数组大小。
    \item delete [ ]要与new [ ]要配套使用
\end{itemize}
有了malloc/free为什么还要new/delete?
\begin{itemize}
    \item new运算不需要进行强制类型转换，使用简单方便；
    \item new运算是通过调用构造函数初始化动态创建的对象，执行效率更高；
    \item 使用new能够进行异常处理，使用更安全
\end{itemize}
\begin{remark}
    如果你在函数上面定义了一个指针变量，然后在这个函数里申请了一块动态分配的内存让指针指向它。实际上，这个指针的地址是在栈上，但是它所指向的内容却是在堆上面。这一点要注意！所以，在一个函数里申请了空间后，比如：
    \begin{cpp}
        void Function(void)
        {
                char *p = (char *)malloc(100 * sizeof(char));
            }
    \end{cpp}
    千万不要认为函数返回，函数所在的栈被销毁指针也跟着销毁，申请的内存也就一样跟着销毁了。这是错误的。因为申请的内存在堆上，除非程序员手动释放，否则要等到程序结束释放所有内存才会释放它们，跟函数是否结束没有关系。也就是函数所在的栈被销毁跟堆完全没有关系。所以，忠告就是：使用完不再需要记得释放动态分配的内存！在该部分程序退出之前释放内存并立即给P赋0值(NULL)。另一个办法是保证P在没有初始化之前，将不再被使用。
\end{remark}
\subsection{常见C++错误和警告}
%\cppfile{warn/warn1.cpp}
\begin{Textcode}
    warn1.cxx:16:12: warning: address of stack memory associated with local
    variable 'result' returned [-Wreturn-stack-address]
    return result;
    ^~~~~~
    1 warning generated.
\end{Textcode}
在函数中result为局部变量，通常的局部变量存储在stack中，函数调用结束后变量的存储单元就会被系统回收。上面的函数返回一个stack上的地址，这个地址在函数调用结束后就会被用作其他功能，这样调用该函数时会出错。
\cppfile{code/test1.cpp}
